using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using 后端服务.Models;

namespace 后端服务.Services
{
    /// <summary>
    /// JWT（JSON Web Token）服务类，处理令牌的生成、刷新和验证
    /// </summary>
    public class JwtService
    {
        private readonly string _secretKey;        // JWT签名密钥
        private readonly int _expireMinutes;       // 令牌过期时间（分钟）
        private readonly SqlQueryService _sqlQueryService;    // SQL查询服务
    
        /// <summary>
        /// 构造函数，初始化JWT服务
        /// </summary>
        /// <param name="configuration">配置信息，用于获取JWT设置</param>
        /// <param name="sqlQueryService">SQL查询服务</param>
        public JwtService(IConfiguration configuration, SqlQueryService sqlQueryService)
        {
            _secretKey = configuration["Jwt:Key"] ?? throw new ArgumentNullException("JWT密钥未配置");
            _expireMinutes = int.Parse(configuration["Jwt:ExpireMinutes"] ?? "15");
            _sqlQueryService = sqlQueryService;
        }

        /// <summary>
        /// 为用户生成JWT访问令牌
        /// </summary>
        /// <param name="user">用户对象</param>
        /// <returns>JWT字符串</returns>
        public string GenerateToken(User user)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_secretKey);
        
            // 配置令牌描述符
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                // 设置令牌声明（用户信息）
                Subject = new ClaimsIdentity(new[]
                {
                    new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString()),
                    new Claim(ClaimTypes.Name, user.Username),
                    new Claim(ClaimTypes.Role, user.IsAdmin ? "Admin" : "User")
                }),
                // 设置过期时间
                Expires = DateTime.UtcNow.AddMinutes(_expireMinutes),
                // 设置签名凭证
                SigningCredentials = new SigningCredentials(
                    new SymmetricSecurityKey(key), 
                    SecurityAlgorithms.HmacSha256Signature)
            };
        
            // 创建并返回令牌
            var token = tokenHandler.CreateToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }

        /// <summary>
        /// 生成刷新令牌
        /// </summary>
        /// <param name="user">用户对象</param>
        /// <param name="deviceInfo">设备信息</param>
        /// <returns>刷新令牌字符串</returns>
        public async Task<string> GenerateRefreshToken(User user, string deviceInfo = "")
        {
            // 生成随机刷新令牌
            var randomNumber = new byte[32];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            var refreshToken = Convert.ToBase64String(randomNumber);

            // 清理该用户的过期令牌
            var deleteExpiredTokensSql = @"
                DELETE FROM RefreshTokens 
                WHERE UserId = :userId AND Expires <= :currentDate";
            await _sqlQueryService.ExecuteAsync(deleteExpiredTokensSql, new Dictionary<string, object>
            {
                { ":userId", user.UserId },
                { ":currentDate", DateTime.UtcNow }
            });

            // 创建刷新令牌实体并保存到数据库
            var insertTokenSql = @"
                INSERT INTO RefreshTokens (UserId, Token, Expires, CreatedDate, DeviceInfo, UseCount)
                VALUES (:userId, :token, :expires, :createdDate, :deviceInfo, :useCount)";
            await _sqlQueryService.ExecuteAsync(insertTokenSql, new Dictionary<string, object>
            {
                { ":userId", user.UserId },
                { ":token", refreshToken },
                { ":expires", DateTime.UtcNow.AddDays(7) },
                { ":createdDate", DateTime.UtcNow },
                { ":deviceInfo", deviceInfo },
                { ":useCount", 0 }
            });

            return refreshToken;
        }

        /// <summary>
        /// 使用刷新令牌获取新的访问令牌
        /// </summary>
        /// <param name="refreshToken">刷新令牌</param>
        /// <param name="deviceInfo">设备信息（可选）</param>
        /// <returns>如果刷新成功，返回用户信息、新的访问令牌和新的刷新令牌；否则返回null</returns>
        public async Task<(User user, string token, string newRefreshToken)?> RefreshAccessToken(string refreshToken, string deviceInfo = "")
        {
            // 查找有效的刷新令牌
            var getTokenSql = @"
                SELECT TokenId, UserId, Token, Expires, CreatedDate, DeviceInfo, UseCount
                FROM RefreshTokens 
                WHERE Token = :token AND Expires > :currentDate";
            var tokenEntities = await _sqlQueryService.QueryAsync<RefreshToken>(getTokenSql, new Dictionary<string, object>
            {
                { ":token", refreshToken },
                { ":currentDate", DateTime.UtcNow }
            });

            var tokenEntity = tokenEntities.FirstOrDefault();
            if (tokenEntity == null)
                return null;

            // 查询对应的用户信息
            var getUserSql = @"
                SELECT UserId, Username, PasswordHash, CreatedDate, IsAdmin
                FROM Users
                WHERE UserId = :userId";
            var users = await _sqlQueryService.QueryAsync<User>(getUserSql, new Dictionary<string, object>
            {
                { ":userId", tokenEntity.UserId }
            });

            var user = users.FirstOrDefault();
            if (user == null)
                return null;
            if (user == null)
                return null;

            // 如果提供了设备信息，则进行验证
            if (!string.IsNullOrEmpty(deviceInfo) && !string.IsNullOrEmpty(tokenEntity.DeviceInfo) 
                && tokenEntity.DeviceInfo != deviceInfo)
            {
                // 设备信息不匹配，可能是令牌被盗用
                await DeleteRefreshToken(tokenEntity.Token);
                return null;
            }

            // 更新使用次数
            tokenEntity.UseCount++;

            // 检查使用次数是否异常（例如超过100次）
            if (tokenEntity.UseCount > 100)
            {
                await DeleteRefreshToken(tokenEntity.Token);
                return null;
            }

            // 生成新的访问令牌和刷新令牌
            var newToken = GenerateToken(user);
            var newRefreshToken = await GenerateRefreshToken(user, deviceInfo);

            // 使旧的刷新令牌失效
            await DeleteRefreshToken(tokenEntity.Token);

            return (user, newToken, newRefreshToken);
        }

        private async Task DeleteRefreshToken(string token)
        {
            var deleteTokenSql = "DELETE FROM RefreshTokens WHERE Token = :token";
            await _sqlQueryService.ExecuteAsync(deleteTokenSql, new Dictionary<string, object>
            {
                { ":token", token }
            });
        }

    }
}